(* begin global tools *)


let print_list l = List.map print_endline l

(* end global tools *)

(* begin databases *)

module StringMap = Map.Make (String)

let duplicates = Hashtbl.create 300
let database = Hashtbl.create 30000

let print_duplicates = fun () ->
  let f = fun key value -> print_endline ("\n"^(String.concat "\n" value)) in
  Hashtbl.iter f duplicates

let browse_duplicates = fun () ->
  let table_length_s = string_of_int (Hashtbl.length duplicates) in
  let item = ref 1 in
  let f = fun key value -> 
    let paths = Array.of_list value in
    let prompt = "\nSet "^(string_of_int !item)^" of "^table_length_s^", preserve files [1 - "^(string_of_int (Array.length paths))^", all]: " in
    let print = fun i path -> print_endline ("["^(string_of_int (i+1))^"] "^path) in
    let _ = Array.iteri print paths; print_string prompt; item:= !item + 1 in
    let n = (read_int() -1) in
    let purge = fun i path -> if i = n then () else Unix.unlink paths.(i) in
    Array.iteri purge paths; print_newline() in
  Hashtbl.iter f duplicates

(** Start of LazyDigest module **)

(*module LazyDigest =
   struct


   end;;*)


(** Start of LazyDigest module **)

(* end databases *)

let rec get_complete_path = fun current_dir given_path ->
  if given_path = "." then
    current_dir
  else
    if String.length given_path = 1 then
      Filename.concat current_dir given_path
    else
      match (String.get given_path 0, String.get given_path 1) with
          ('.' ,'/') -> Filename.concat current_dir (Tools.rest_of_string given_path 2)
        | ('/', '/') -> get_complete_path current_dir (Tools.rest_of_string given_path 1)
        | ('/' ,_)   -> given_path
        | (_, _)     -> Filename.concat current_dir given_path

let rec insert = fun info path ->
  try
    let map = Hashtbl.find database info.Unix.st_size in
    try
      let unchecked_path = List.hd (StringMap.find "" map) in
      let unchecked_path_md5sum = LazyDigest.digest_lookup info unchecked_path in
      let new_map = StringMap.add unchecked_path_md5sum [unchecked_path] StringMap.empty in
      let _ = Hashtbl.replace database info.Unix.st_size new_map in
      insert info path
    with Not_found ->
      let path_md5sum = LazyDigest.digest_lookup info path in
      try
	let previous_paths = StringMap.find path_md5sum map in
        let dupes = path::previous_paths in
	let _ = Hashtbl.replace duplicates path_md5sum dupes in
	let new_map = StringMap.add path_md5sum dupes map in
        Hashtbl.add database info.Unix.st_size new_map
      with Not_found ->
	let new_map = StringMap.add path_md5sum [path] map in
	Hashtbl.add database info.Unix.st_size new_map
  with Not_found ->
    let map = StringMap.add "" [path] StringMap.empty in
    Hashtbl.add database info.Unix.st_size map


let browse = fun path ->
  let rec loop = fun current_dir paths subdirs ->
    match (paths, subdirs) with
	([], []) -> ()
      | ([], d::rd) ->
          let content = List.map (get_complete_path d) (Array.to_list (Sys.readdir d)) in
          loop d content rd
      | (p::rp, sd) ->
	  let info = Unix.lstat p in
	  match info.Unix.st_kind with
	      Unix.S_DIR -> loop current_dir rp (p::sd)
	    | Unix.S_REG -> let _ = insert info p in  loop current_dir rp sd
	    | _ -> loop current_dir rp sd
  in loop path [] [path]

(* run *)

let _ = 
  match Array.length Sys.argv with
      0 -> print_endline "there was an error somewhere"
    | 2 ->
	 LazyDigest.load_digest_db ();
         browse (get_complete_path (Sys.getcwd ()) (Sys.argv.(1)));
	 LazyDigest.store_digest_db ()
    | _ -> print_endline "usage: mldupes path"

(* let _ = browse_duplicates () *)

let _ = print_endline LazyDigest.test
